/* 莫队 O(n*sqrt(m))
    离线算法 分块+暴力
* 1.暴力做法
    记录一个 cnt[x] 数组，表示当前区间 x 出现的次数，
    在两个区间转移时，即左右指针分别移动到对应区间的同时更新 cnt[x]，同时更新答案

* 2.分块策略：将所有区间按左端点分块，同时块内的区间右端点排序再暴力求解

* 3.玄学优化
    对于奇数块内部右端点按照从小到大排序，
    对于偶数块内部右端点按照从大到小排序，
    奇数块和偶数块可以互换

    解释:在第一次滚动时，右端点大概率靠右，如果直接右端点再回到左侧重新滚到右，会多滚一次
                                        如果从右向左滚动，只滚动两次

* 本题:回滚莫队
*/
#define DEBUG
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
using ll=long long;
const int N=1e5+10;

inline int read()
{
	int x=0;char c=getchar();
	while (c<'0'||c>'9') c=getchar();
	while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48,c=getchar();
	return x;
}
inline void write(int x)
{
	if (x>=10) write(x/10);
	putchar(x%10+48);
}//快读快输

int n, m, len;
int w[N], cnt[N]; ll res[N];
// 原序列 当前维护区间内每个数出现的次数
inline int get(int x)
{
    return x/len;
}

struct Query
{
    int id, l, r;
    bool operator<(const Query &q) const {
        int l1 = get(l), r1 = get(r);
        int l2 = get(q.l), r2 = get(q.r);
        if (l1 != l2) return l1 < l2;
        return r1 < r2;
    }
} query[N];

vector<int> nums; //离散化

inline void printonequery(int i)
{
    printf("query[%d]: %d %d\n", query[i].id, query[i].l, query[i].r);
}
inline void printquery()
{
    for(int i=1; i<=m; i++)
        printonequery(i);
}

inline void add(int x, ll &res)
{
    res=max(res, 1ll*nums[x]*++cnt[x]);  //重要度公式计算
}

inline void del(int x)
{
    --cnt[x];
}

signed main()
{
    #ifdef DEBUG
        freopen("./in.txt","r",stdin);
        freopen("./out.txt","w",stdout);
    #endif

    n=read(); m=read(); len=sqrt(n);
    for(int i=1; i<=n; i++) w[i]=read(), nums.push_back(w[i]);
    //离散化
    sort(nums.begin(), nums.end());
    nums.erase(unique(nums.begin(), nums.end()), nums.end());

    //替换为离散化后的数
    for(int i=1; i<=n; i++)
        w[i]=lower_bound(nums.begin(), nums.end(), w[i])-nums.begin();

    for(int i=1; i<=m; i++)
    {
        int l=read(), r=read();
        query[i]={i, l, r};
    }
    sort(query+1, query+m+1);

    //回滚莫队
    for(int x=1; x<=m;) //第x个询问
    {
        int y=x; //左端点再同一块内的询问
        while(y<=m&&get(query[y].l)==get(query[x].l)) y++; //左端点再同一块内
        int right=get(query[x].l)*len+len-1; //当前块的最后一个位置

        //暴力解决块内询问
        while(x<y&&query[x].r<=right)
        {
            int l=query[x].l, r=query[x].r, id=query[x].id;
            ll ans=0;
            for(int i=l; i<=r; i++) add(w[i], ans); //加入块内所有数
            res[id]=ans;
            for(int i=l; i<=r; i++) del(w[i]); //还原
            x++; //计算下一个询问
        }
    
        //块间询问
        ll ans=0;
        int i=right+1,j=right;
        while(x<y) //右端点再当前块外，使用暴力回滚
        {
            int l=query[x].l, r=query[x].r, id=query[x].id;

            // printf("%d [%d,%d] [%d,%d]\n", id, l, r, i, j);
            while(j<r) add(w[++j], ans); //将查询区间右边块外部分的数加入
            ll backup=ans; //备份当前情况下的答案
            while(i>l) add(w[--i], ans); //将查询区间左边块内部分的数加入
            res[id]=ans; //记录答案
            while(i<right+1) del(w[i++]); //将左边块内部分的数删去
            ans=backup; //右端点只会增大
            x++;
        }
        memset(cnt, 0, sizeof cnt);//对于每个块，最开始维护区间都是空的，因此将重置 cnt 数组
    }
    for(int i=1; i<=m; i++)
        write(res[i]), puts(""); //printf("%d\n", res[i]);
    return 0;
}   